package com.eight.cloud.security.config;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.eight.cloud.core.global.model.LoginUser;
import com.eight.cloud.redisson.utils.TokenManager;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.security.authorization.AuthorizationDecision;
import org.springframework.security.authorization.AuthorizationManager;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.access.intercept.RequestAuthorizationContext;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import java.util.Map;
import java.util.Set;
import java.util.function.Supplier;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

import static com.eight.cloud.core.global.constant.StrConstant.*;

/**
 * 基于url的权限认证
 *
 * @ClassName: BaseUrlAuthorizationManager
 * @Author: TXC
 * @Date: 2024-11-03 14:40
 **/
@Slf4j
@AutoConfiguration
public class BaseUrlAuthorizationManager implements AuthorizationManager<RequestAuthorizationContext> {
    private final TokenManager tokenManager;
    @Value("${spring.application.name}")
    private String module;

    private static final Pattern RESOURCE_PATTERN = Pattern.compile("[^:]+(:[^:]+)*");

    public BaseUrlAuthorizationManager(TokenManager tokenManager) {
        this.tokenManager = tokenManager;
    }

    @Override
    public void verify(Supplier<Authentication> authentication, RequestAuthorizationContext object) {
        AuthorizationManager.super.verify(authentication, object);
    }

    @Override
    public AuthorizationDecision check(Supplier<Authentication> authentication,
                                       RequestAuthorizationContext requestContext) {
        HttpServletRequest request = requestContext.getRequest();
        String source = request.getHeader(SOURCE);
        if (StrUtil.isNotBlank(source) || FEIGN.equals(source)) {
            return new AuthorizationDecision(true);
        }
        final String pathInfo = request.getRequestURI();
        String method = request.getMethod();
        LoginUser user = tokenManager.getLoginUser(request);
        if (ObjectUtil.isNull(user)) {
            return new AuthorizationDecision(false);
        }
        String resource = module + pathInfo.replace(SLASH, COLON) + COLON + method.toLowerCase();
        return new AuthorizationDecision(checkPermission(resource, user.obtainPermissionsMap()));
    }

    private boolean checkPermission(String resource, Map<String, Boolean> permissionsMap) {
        if (permissionsMap.containsKey(resource)) {
            return permissionsMap.get(resource);
        }
        return checkPermission(resource, permissionsMap.keySet());
    }

    /**
     * @Description: 权限校验 校验的是资源是否在权限列表中 接口格式模板：/user/{id}/get
     * @param resource 资源
     * @param permissions 权限列表
     * @return 是否有权限
     */
    private boolean checkPermission(String resource, Set<String> permissions) {
        if (StrUtil.isBlank(resource) || ObjectUtil.isEmpty(permissions)) {
            return false;
        }
        if (!RESOURCE_PATTERN.matcher(resource).matches()) {
            throw new IllegalArgumentException("Invalid resource format: " + resource);
        }
        String[] resourceParts = resource.split(COLON);
        permissions = permissions.parallelStream().filter(permission ->
                permission.startsWith(module) && (permission.contains(LEFT_BRACE) || permission.contains(RIGHT_BRACE) || permission.contains(ASTERISK_STAR))
        ).collect(Collectors.toSet());
        for (String permission : permissions) {
            if (StrUtil.isBlank(permission)) {
                continue;
            }
            if (!RESOURCE_PATTERN.matcher(permission).matches()) {
                throw new IllegalArgumentException("Invalid permission format: " + permission);
            }
            String[] permissionParts = permission.split(COLON);
            return compareCodeParts(resourceParts, permissionParts);
        }
        return false;
    }

    /**
     * 对比资源和权限的编码
     * @param resourceParts 资源编码
     * @param permissionParts 权限编码
     * @return 是否匹配
     */
    private static boolean compareCodeParts(String[] resourceParts, String[] permissionParts) {
        if (resourceParts.length != permissionParts.length) {
            // 路径长度不一致，无法匹配
            return false;
        }
        for (int i = 0; i < resourceParts.length; i++) {
            if (!partMatched(resourceParts[i], permissionParts[i])) {
                // 路径部分不匹配
                return false;
            }
        }
        return true;
    }

    /**
     * 对比路径部分
     * @param resourcePart 资源路径部分
     * @param permissionPart 权限路径部分
     * @return 是否匹配
     */
    private static boolean partMatched(String resourcePart, String permissionPart) {
        String regex = "\\*|\\{.*}|" + Pattern.quote(resourcePart);
        return permissionPart.matches(regex);
    }
}
